package com.yiruantong.common.redis.config;

import cn.hutool.core.util.ObjectUtil;
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.jsontype.impl.LaissezFaireSubTypeValidator;
import lombok.extern.slf4j.Slf4j;
import com.yiruantong.common.redis.config.properties.RedissonProperties;
import com.yiruantong.common.redis.handler.KeyPrefixHandler;
import com.yiruantong.common.redis.manager.PlusSpringCacheManager;
import org.redisson.client.codec.StringCodec;
import org.redisson.codec.CompositeCodec;
import org.redisson.codec.TypedJsonJacksonCodec;
import org.redisson.spring.starter.RedissonAutoConfigurationCustomizer;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.Bean;

/**
 * redis配置
 *
 * @author YiRuanTong
 */
@Slf4j
@AutoConfiguration
@EnableCaching
@EnableConfigurationProperties(RedissonProperties.class)
public class RedisConfig {

  @Autowired
  private RedissonProperties redissonProperties;

  @Autowired
  private ObjectMapper objectMapper;

  @Bean
  public RedissonAutoConfigurationCustomizer redissonCustomizer() {
    return config -> {
      ObjectMapper om = objectMapper.copy();
      om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
      // 指定序列化输入的类型，类必须是非final修饰的。序列化时将对象全类名一起保存下来
      om.activateDefaultTyping(LaissezFaireSubTypeValidator.instance, ObjectMapper.DefaultTyping.NON_FINAL);
      TypedJsonJacksonCodec jsonCodec = new TypedJsonJacksonCodec(Object.class, om);
      // 组合序列化 key 使用 String 内容使用通用 json 格式
      CompositeCodec codec = new CompositeCodec(StringCodec.INSTANCE, jsonCodec, jsonCodec);
      config.setThreads(redissonProperties.getThreads())
        .setNettyThreads(redissonProperties.getNettyThreads())
        .setCodec(codec);
      RedissonProperties.SingleServerConfig singleServerConfig = redissonProperties.getSingleServerConfig();
      if (ObjectUtil.isNotNull(singleServerConfig)) {
        // 使用单机模式
        config.useSingleServer()
          //设置redis key前缀
          .setNameMapper(new KeyPrefixHandler(redissonProperties.getKeyPrefix()))
          .setTimeout(singleServerConfig.getTimeout())
          .setClientName(singleServerConfig.getClientName())
          .setIdleConnectionTimeout(singleServerConfig.getIdleConnectionTimeout())
          .setSubscriptionConnectionPoolSize(singleServerConfig.getSubscriptionConnectionPoolSize())
          .setConnectionMinimumIdleSize(singleServerConfig.getConnectionMinimumIdleSize())
          .setConnectionPoolSize(singleServerConfig.getConnectionPoolSize());
      }
      // 集群配置方式 参考下方注释
      RedissonProperties.ClusterServersConfig clusterServersConfig = redissonProperties.getClusterServersConfig();
      if (ObjectUtil.isNotNull(clusterServersConfig)) {
        config.useClusterServers()
          //设置redis key前缀
          .setNameMapper(new KeyPrefixHandler(redissonProperties.getKeyPrefix()))
          .setTimeout(clusterServersConfig.getTimeout())
          .setClientName(clusterServersConfig.getClientName())
          .setIdleConnectionTimeout(clusterServersConfig.getIdleConnectionTimeout())
          .setSubscriptionConnectionPoolSize(clusterServersConfig.getSubscriptionConnectionPoolSize())
          .setMasterConnectionMinimumIdleSize(clusterServersConfig.getMasterConnectionMinimumIdleSize())
          .setMasterConnectionPoolSize(clusterServersConfig.getMasterConnectionPoolSize())
          .setSlaveConnectionMinimumIdleSize(clusterServersConfig.getSlaveConnectionMinimumIdleSize())
          .setSlaveConnectionPoolSize(clusterServersConfig.getSlaveConnectionPoolSize())
          .setReadMode(clusterServersConfig.getReadMode())
          .setSubscriptionMode(clusterServersConfig.getSubscriptionMode());
      }
      log.info("初始化 redis 配置");
    };
  }

  /**
   * 自定义缓存管理器 整合spring-cache
   */
  @Bean
  public CacheManager cacheManager() {
    return new PlusSpringCacheManager();
  }

  /**
   * redis集群配置 yml
   *
   * --- # redis 集群配置(单机与集群只能开启一个另一个需要注释掉)
   * spring:
   *   redis:
   *     cluster:
   *       nodes:
   *         - 192.168.0.100:6379
   *         - 192.168.0.101:6379
   *         - 192.168.0.102:6379
   *     # 密码
   *     password:
   *     # 连接超时时间
   *     timeout: 10s
   *     # 是否开启ssl
   *     ssl: false
   *
   * redisson:
   *   # 线程池数量
   *   threads: 16
   *   # Netty线程池数量
   *   nettyThreads: 32
   *   # 集群配置
   *   clusterServersConfig:
   *     # 客户端名称
   *     clientName: ${rattan.name}
   *     # master最小空闲连接数
   *     masterConnectionMinimumIdleSize: 32
   *     # master连接池大小
   *     masterConnectionPoolSize: 64
   *     # slave最小空闲连接数
   *     slaveConnectionMinimumIdleSize: 32
   *     # slave连接池大小
   *     slaveConnectionPoolSize: 64
   *     # 连接空闲超时，单位：毫秒
   *     idleConnectionTimeout: 10000
   *     # 命令等待超时，单位：毫秒
   *     timeout: 3000
   *     # 发布和订阅连接池大小
   *     subscriptionConnectionPoolSize: 50
   *     # 读取模式
   *     readMode: "SLAVE"
   *     # 订阅模式
   *     subscriptionMode: "MASTER"
   */

}
